'****************************************************************************'
'                                                                            '
' Copyright (C) 2010 Muhammad alaa                                           '
'                                                                            '
' This program is free software; you can redistribute it and/or modify       '
' it under the terms of the GNU General Public License as published by       '
' the Free Software Foundation; either version 2 of the License, or          '
' (at your option) any later version.                                        '
'                                                                            '
' This program is distributed in the hope that it will be useful,            '
' but WITHOUT ANY WARRANTY; without even the implied warranty of             '
' MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the              '
' GNU General Public License for more details.                               '
'                                                                            '
' You should have received a copy of the GNU General Public License along    '
' with this program; if not, write to the Free Software Foundation, Inc.,    '
' 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.                '
'                                                                            '
'****************************************************************************'

Imports System.Text

Namespace DataTypes

    ''' <summary>String Type</summary>
    Public Enum StringType
        ''' <summary>Length Prefixed String</summary>
        BStr
        ''' <summary>Null Terminated String</summary>
        NullTerminated
    End Enum

    ''' <summary>String Format</summary>
    Public Enum StringFormat
        ''' <summary>Unicode String</summary>
        Unicode
        ''' <summary>ANSI String</summary>
        Ansi
    End Enum

    ''' <summary>
    ''' TString is system depended string
    ''' </summary>
    Public NotInheritable Class TString
        Implements IDisposable

        Private mValue As String = Nothing
        Private mType As StringType = StringType.NullTerminated
        Private mFormat As StringFormat = StringFormat.Ansi
        Private mHandle As IntPtr = IntPtr.Zero
        Private mBufSize As Integer = 0

#Region "IDisposable Support"
        Private disposedValue As Boolean = False

        Public Sub Dispose() Implements IDisposable.Dispose
            Dispose(True)
            GC.SuppressFinalize(Me)
        End Sub

        Protected Sub Dispose(ByVal disposing As Boolean)
            If Not Me.disposedValue Then
                If mHandle <> IntPtr.Zero Then
                    If mType = StringType.BStr Then
                        mHandle = New IntPtr(mHandle.ToInt32 - 4)
                    End If
                    Marshal.FreeHGlobal(mHandle)
                    mHandle = IntPtr.Zero
                    mBufSize = 0
                End If
            End If
            Me.disposedValue = True
        End Sub
#End Region

        ''' <summary>
        ''' Initialize new instance with string as null-terminated ansi string
        ''' </summary>
        ''' <param name="value">string value</param>
        Public Sub New(ByVal value As String)
            Me.New(value, StringType.NullTerminated, StringFormat.Ansi)
        End Sub

        ''' <summary>
        ''' Initialize new instance
        ''' </summary>
        ''' <param name="value">string value</param>
        ''' <param name="type">string type</param>
        ''' <param name="format">string format</param>
        Public Sub New(ByVal value As String, ByVal type As StringType, ByVal format As StringFormat)
            mValue = value
            mType = type
            mFormat = format
            ChangeType(mType, mFormat)
        End Sub

        ''' <summary>
        ''' get current string handle in the unmanaged memory
        ''' </summary>
        Public ReadOnly Property Handle() As IntPtr
            Get
                Return mHandle
            End Get
        End Property

        ''' <summary>
        ''' string size in memory
        ''' </summary>
        Public ReadOnly Property BufferSize() As Integer
            Get
                Return mBufSize
            End Get
        End Property

        ''' <summary>
        ''' get number of characters in string
        ''' </summary>
        Public ReadOnly Property StringLength() As Integer
            Get
                Return mValue.Length
            End Get
        End Property

        ''' <summary>
        ''' get current string type
        ''' </summary>
        Public ReadOnly Property Type() As StringType
            Get
                Return mType
            End Get
        End Property

        ''' <summary>
        ''' get current string format
        ''' </summary>
        Public ReadOnly Property Format() As StringFormat
            Get
                Return mFormat
            End Get
        End Property

        ''' <summary>
        ''' change current string type to new type
        ''' </summary>
        ''' <param name="type">new type to change to</param>
        Public Sub ChangeType(ByVal type As StringType, ByVal format As StringFormat)
            If mValue.Length = 0 Then
                Throw New Exception("Empty String not allowed")
            End If

            If type = StringType.BStr AndAlso format = StringFormat.Ansi Then
                Throw New Exception("BStr is only unicode string")
            End If

            If mHandle <> IntPtr.Zero Then

                If mType = type AndAlso mFormat = format Then
                    Return
                End If

                If mType = StringType.BStr Then
                    mHandle = New IntPtr(mHandle.ToInt32 - 4)
                End If
                Marshal.FreeHGlobal(mHandle)
                mHandle = IntPtr.Zero
                mBufSize = 0
            End If


            Dim str() As Byte = Nothing

            If type = StringType.BStr Then
                ' Bstr Format is
                ' 1 - 4-bytes lead with size of string
                ' 2 - the string itself
                ' 3 - 2-bytes null chars

                mBufSize = 6 + mValue.Length * 2
                ReDim str(mBufSize - 1)

                Dim StrByteLen As Integer = mValue.Length * 2

                ' set str length
                ' byte 0 is Hi-order byte
                ' byte 3 is low-order byte
                str(0) = CByte((StrByteLen << 24) >> 24)
                str(1) = CByte((StrByteLen << 16) >> 24)
                str(2) = CByte((StrByteLen << 8) >> 24)
                str(3) = CByte(StrByteLen >> 24)
                ' set terminator (NOT NEEDED ALREADY ZERO)
                'str(str.Length - 1) = 0
                'str(str.Length - 2) = 0
                ' set the string
                Encoding.Unicode.GetBytes(mValue, 0, mValue.Length, str, 4)

            ElseIf type = StringType.NullTerminated Then
                If format = StringFormat.Ansi Then
                    str = Encoding.ASCII.GetBytes(mValue)
                    ReDim Preserve str(str.Length)
                    mBufSize = str.Length

                Else 'format = StringFormat.Unicode
                    str = Encoding.Unicode.GetBytes(mValue)
                    ReDim Preserve str(str.Length + 1)
                    mBufSize = str.Length
                End If
            End If

            mHandle = Marshal.AllocHGlobal(mBufSize)
            Marshal.Copy(str, 0, mHandle, mBufSize)

            ' if type is bstr
            If mType = StringType.BStr Then
                ' skip the lead integer
                mHandle = New IntPtr(mHandle.ToInt32 + 4)
            End If

            mType = type
            mFormat = format
        End Sub

        ''' <summary>
        ''' Get TString value
        ''' </summary>
        Public Overrides Function ToString() As String
            Return mValue
        End Function

        ''' <summary>
        ''' Cast TString to IntPtr
        ''' </summary>
        ''' <param name="Obj">TString to cast</param>
        ''' <returns>TString pointer in unmanaged memory</returns>
        Public Shared Narrowing Operator CType(ByVal Obj As TString) As IntPtr
            Return Obj.mHandle
        End Operator

        ''' <summary>
        ''' Cast String to TString
        ''' </summary>
        ''' <param name="value">String to cast</param>
        ''' <returns>TString object</returns>
        Public Shared Narrowing Operator CType(ByVal value As String) As TString
            Return New TString(value)
        End Operator

        ''' <summary>
        ''' Cast TString to String
        ''' </summary>
        ''' <param name="Obj">TString to cast</param>
        ''' <returns>String obj</returns>
        Public Shared Narrowing Operator CType(ByVal Obj As TString) As String
            Return Obj.ToString
        End Operator


    End Class
End Namespace